package com.ljx.blog.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljx.blog.common.domain.vo.resp.PageResp;
import com.ljx.blog.common.enums.CommonStatusTypeEnum;
import com.ljx.blog.user.domain.dto.UserFlagDto;
import com.ljx.blog.user.domain.vo.req.UserPageReq;
import com.ljx.blog.user.domain.vo.resp.UserRoleResp;
import com.ljx.blog.user.enums.TenantRoleEnum;
import com.ljx.blog.common.exception.ApiException;
import com.ljx.blog.security.common.UserContext;
import com.ljx.blog.security.util.UserUtil;
import com.ljx.blog.user.domain.dto.UserDto;
import com.ljx.blog.user.domain.entity.Role;
import com.ljx.blog.user.domain.entity.Tenant;
import com.ljx.blog.user.domain.entity.User;
import com.ljx.blog.user.domain.vo.req.AuthReq;
import com.ljx.blog.user.domain.vo.req.UserSubmitReq;
import com.ljx.blog.user.domain.vo.resp.LoginResp;
import com.ljx.blog.user.domain.vo.resp.UserResp;
import com.ljx.blog.user.enums.AuthTypeEnum;
import com.ljx.blog.user.mapper.UserMapper;
import com.ljx.blog.user.service.*;
import com.ljx.blog.user.service.impl.login.AccountAuthServiceImpl;
import com.ljx.blog.user.service.impl.login.EmailAuthServiceImpl;
import com.ljx.blog.user.service.impl.login.PhoneAuthServiceImpl;
import com.ljx.blog.user.service.impl.login.WxAuthServiceImpl;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: LiJunXi
 * @Description:
 * @Date: 2024-10-12  15:24
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private TenantService tenantService;

    @Override
    public LoginResp login(AuthReq authReq) {
        AuthService authService = getAuthService(authReq);
        User user = authService.tryToLogin(authReq);
        Long tokenId = RandomUtils.nextLong();
        String token = UserUtil.createToken(user.getId(), tokenId);
        UserDto userDto = saveUserCache(user, tokenId);

        return LoginResp.builder()
                .token(token)
                .userInfo(userDto)
                .build();
    }

    private UserDto saveUserCache(User user, Long tokenId) {
        UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
        userDto.setPermissions(UserUtil.tryToGetUserPermission(user.getId()));
        userDto.setRoles(UserUtil.tryToGetUserRole(user.getId()));
        userDto.setMenus(UserUtil.tryToGetUserMenu(user.getId()));
        UserUtil.setTokenCache(userDto, tokenId);

        return userDto;
    }

    @Override
    public UserResp getInfo(Long userId) {
        if (Objects.isNull(userId)){
            throw new ApiException("用户ID为空");
        }
        User user = userMapper.selectById(userId);
        UserResp userResp = BeanUtil.copyProperties(user, UserResp.class);
        return userResp;
    }



    @Override
    public UserResp register(AuthReq authReq) {
        AuthService authService = getAuthService(authReq);
        User user = authService.tryToRegister(authReq);

        return BeanUtil.copyProperties(user, UserResp.class);
    }

    private AuthService getAuthService(AuthReq authReq) {
        if (AuthTypeEnum.ACCOUNT.getKey().equals(authReq.getAuthType())){
            return new AccountAuthServiceImpl();
        } else if (AuthTypeEnum.EMAIL.getKey().equals(authReq.getAuthType())){
            return new EmailAuthServiceImpl();
        } else if (AuthTypeEnum.PHONE.getKey().equals(authReq.getAuthType())){
            return new PhoneAuthServiceImpl();
        } else if (AuthTypeEnum.WX.getKey().equals(authReq.getAuthType())){
            return new WxAuthServiceImpl();
        } else {
            throw new ApiException("无效认证方式");
        }
    }

    @Override
    public void checkPerAndRoleAndMenu(UserDto userDto) {
        UserDto user = UserContext.getUser();
        if (CollUtil.isNotEmpty(userDto.getPermissions()) && !CollUtil.isEqualList(user.getPermissions(), userDto.getPermissions())){
            throw new ApiException("权限信息发生变化，请重新登录");
        }
        if (CollUtil.isNotEmpty(userDto.getRoles()) && !CollUtil.isEqualList(user.getRoles(), userDto.getRoles())){
            throw new ApiException("角色信息发生变化，请重新登录");
        }
        if (CollUtil.isNotEmpty(userDto.getMenus()) && !CollUtil.isEqualList(user.getMenus(), userDto.getMenus())){
            throw new ApiException("菜单信息发生变化，请重新登录");
        }
    }

    @Override
    public User getByUsername(String username) {
        return userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, username)
                .eq(User::getStatus, CommonStatusTypeEnum.NORMAL.getKey()));
    }

    @Override
    public UserResp submitByUsername(UserSubmitReq req) {
        User user = getByUsername(req.getUsername());
        if (Objects.isNull(user)) {
            user = BeanUtil.copyProperties(req, User.class);
            userMapper.insert(user);
            initUserRole(user);
        } else {
            BeanUtils.copyProperties(req, user);
            userMapper.updateById(user);
        }
        return getInfo(user.getId());
    }

    @Override
    public UserResp submit(UserSubmitReq req) {
        User user;
        if (Objects.isNull(req.getId())){
            user = BeanUtil.copyProperties(req, User.class);
            userMapper.insert(user);
            initUserRole(user);
        } else {
            user = userMapper.selectById(req.getId());
            checkUserIsTenantAdmin(user);
            BeanUtils.copyProperties(req, user);
            userMapper.updateById(user);
        }

        return getInfo(user.getId());
    }

    private void checkUserIsTenantAdmin(User user) {
        boolean equals = UserUtil.tryToGetTenantId().equals(user.getTenantId());
        if (!equals){
            throw new ApiException("权限不足");
        }
    }

    private void initUserRole(User user) {
        Tenant tenant = tenantService.getById(user.getTenantId());
        Role role = roleService.getRoleByName(TenantRoleEnum.getUserCode(tenant.getCode()));
        roleService.initUserRole(user.getId(), role.getId());
    }

    @Override
    public PageResp<User> getPage(UserPageReq req, UserFlagDto userFlagDto) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (Objects.nonNull(userFlagDto.getIsSuperUser()) && userFlagDto.getIsSuperUser()){

        } else if (Objects.nonNull(userFlagDto.getIsTenantAdmin()) && userFlagDto.getIsTenantAdmin()){
            lambdaQueryWrapper.eq(User::getTenantId, UserUtil.tryToGetTenantId());
        } else {
            throw new ApiException("权限不足");
        }
        if (Objects.nonNull(req.getKey())){
            lambdaQueryWrapper.like(User::getName, req.getKey());
        }

        Page<User> page = userMapper.selectPage(new Page<>(req.getCurrPage(), req.getPageSize()), lambdaQueryWrapper);

        return PageResp.<User>builder()
                .list(page.getRecords())
                .total(page.getTotal())
                .build();
    }


    @Override
    public List<UserRoleResp> getUserRole(Long userId) {
        List<Long> roleIds = roleService.getUserRoleIdByUserId(userId);
        Map<Long, Long> roleMap = roleIds.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
        List<Role> tenantRoles = roleService.getRoleLikeName(tenantService.getById(UserUtil.tryToGetUserTenantId(userId)).getCode() + "_");
        if (UserUtil.checkIsPlatform()){
            tenantRoles.add(new Role(TenantRoleEnum.PLATFORM.getValue(), TenantRoleEnum.PLATFORM.getKey()));
        }
        return tenantRoles.stream().map(
                item -> new UserRoleResp(item.getId(), item.getName(), roleMap.containsKey(item.getId()))
                ).collect(Collectors.toList());
    }
}
